/* 
 * Autores:
 *      Jonathan Lopez  11106
 *      Jorge Martínez  11237
 * 
 * Archivo: 
 * Última Revision: $Rev$
 *      Fecha: $Date$
 */ 
package Interface;

import Manejo.Transporte.Registros.*;
import Manejo.Transporte.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Scanner;

/**
 * Esta clase contiene funciones utilitarias para pedir y desplegar información
 * al usuario.
 * 
 * @author Jorge Martinez
 * @author Jonathan Lopez
 */  
public class Dialogos {
    private final Registros registros;
    private final Scanner ingreso;
    
    private final RegBarcos registroBarcos;
    private final RegNavieras registroNavieras;
    private final RegPuertos registroPuertos;
    private final RegContenedores registroContenedores;
    private final RegRutas registroRutas;
    //private Contenedor contenedor;

    private final String FINALIZACION = "FINAL";
    
    /**
     * Constructor de la clase.
     * @param registros Registros de todos los objetos.
     * @param ingreso Método de ingreso de datos del usuario.
     */
    public Dialogos(Scanner ingreso, Registros registros) {
        this.registros = registros;
        this.ingreso = ingreso;
        
        registroBarcos = registros.obtenerRegistroBarcos();
        registroNavieras = registros.obtenerRegistroNavieras();
        registroPuertos = registros.obtenerRegistroPuertos();
        registroContenedores = registros.obtenerRegistroContenedores();
        registroRutas = registros.obtenerRegistroRutas();
        
    }
    
    /**
     * Pide al usuario un Objeto de tipo 'ObjetoRegistrable', por lo que este
     * es agregado a su respectivo registro.
     * @param tipoObjeto Tipo de objeto a pedir
     * @return ID del nuevo objeto, o ID.nulo si no se pudo completar la
     * operacion.
     */
    public ID pedirObjeto( ObjetoRegistrable tipoObjeto ){
        try{
            switch( tipoObjeto ){
                case Navieras:
                {
                    // Pedir el nombre de la Naviera
                    String nombreNaviera=pedirNombre( "Ingrese el nombre de la Naviera: ",
                            registroNavieras.obtenerNombreDeNavieras() );
                    String pais=pedirString( "Ingrese el pais de la Naviera: ");
                    Naviera nuevaNaviera = new Naviera(pais, registros);
                    return registroNavieras.agregarNaviera(nuevaNaviera, nombreNaviera);
                }
                case Barcos:
                {
                    // Pedir el nombre del Barco
                    String nombreBarco = pedirNombre( "Ingrese el nombre del Barco: ",
                            registroBarcos.obtenerNombresDeBarcos() );
                    
                    // Pedir capacidad
                    double capacidadBarco = pedirDouble( "Ingrese la capacidad del Barco (en Toneladas métricas): " );
                    
                    // Pedir Naviera
                    ID idNaviera = ID.idNulo;
                    if( pedirDecision( "¿Desea escoger una Naviera para el Barco (S/N)? ") )
                        idNaviera = escogerObjeto( ObjetoRegistrable.Navieras );
                    
                    Barco nuevoBarco = new Barco( capacidadBarco,
                            null, registros);
                    
                    ID idBarco = registroBarcos.agregarBarco(nuevoBarco, nombreBarco);
                    if( !idNaviera.equals(ID.idNulo) ){
                        Naviera nav = registroNavieras.obtenerNaviera(idNaviera);
                        nav.agregarBarco(idBarco);
                    }
                    return idBarco;
                }
                case Puertos:
                {
                    String nombrePuerto=pedirNombre( "Ingrese el nombre del Puerto: ",
                            registroPuertos.obtenerNombresDePuertos() );
                    Puerto nuevoPuerto = new Puerto(pedirLocalizacion(),
                            registros);
                    return registroPuertos.agregarPuerto( nuevoPuerto, nombrePuerto );
                }
                case Contenedores:
                {
                    // Pedir propietario
                    String propietario = pedirString( "Ingrese el propietario del Contenedor: " );
                    
                    // Pedir los productos
                    ArrayList<Producto> productos = new ArrayList<Producto>();
                    do{
                        String descripcion = pedirString( "Ingrese la descripcion del producto: " );
                        double peso = pedirDouble( "Ingrese el peso del producto (1 tonelada métrica ="
                                + " 1000 Kg): " );
                        Producto nuevoProducto = new Producto( peso, descripcion );
                        productos.add(nuevoProducto);
                    }while( pedirDecision( "¿Desea ingresar otro Producto (S/N)? ") );
                    
                    // Pedir puerto destino
                    System.out.println("\n¿A cuál de los siguiente Puertos desea enviar su producto?");
                    ID idPuertoDestino = escogerObjeto( ObjetoRegistrable.Puertos );
                    if( idPuertoDestino.equals(ID.idNulo) )
                        throw new InterruptionException();
                    
                    // Pedir puerto inicial
                    System.out.println("\n¿Desde qué Puerto desea enviar su producto?");
                    ID idPuertoInicial = escogerObjeto( ObjetoRegistrable.Puertos );
                    if( idPuertoInicial.equals(ID.idNulo) )
                        throw new InterruptionException();
                    
                    Puerto puertoInicial = registroPuertos.obtenerPuerto(idPuertoInicial);
                                                           
                    Contenedor nuevoContenedor = new Contenedor( 
                            productos.toArray( new Producto[0] ),
                            idPuertoDestino, propietario,
                            registros);
                    ID idContenedor = registroContenedores.agregarContenedor(nuevoContenedor);
                    puertoInicial.agregarContenedor(idContenedor);
                    
                    return idContenedor;
                }
                case Rutas:
                {
                    Ruta nuevaRuta = new Ruta(registros);
                    
                    // Primer Puerto
                    System.out.println("Puerto de partida");
                    ID idPuertoAnterior = escogerObjeto(ObjetoRegistrable.Puertos);
                    if( idPuertoAnterior.equals(ID.idNulo) )
                        throw new InterruptionException();
                    
                    nuevaRuta.agregarPuerto(idPuertoAnterior, 0);
                    
                    // Demas puertos
                    do{
                        System.out.println("Siguiente Puerto");
                        ID idPuertoNuevo = escogerObjeto(ObjetoRegistrable.Puertos);
                        if( idPuertoNuevo.equals(ID.idNulo) )
                            throw new InterruptionException();
                        
                        /* Si el Puerto es el mismo */
                        while( idPuertoNuevo.equals(idPuertoAnterior) ){
                            System.out.println("\t**ERROR** - No se puede escoger "
                                    + "el mismo Puerto dos veces seguidas.");
                            System.out.println("Vuelva a escoger un Puerto");
                            idPuertoNuevo = escogerObjeto(ObjetoRegistrable.Puertos);
                        }
                        
                        // Validar el Puerto nuevo
                        if( idPuertoNuevo.equals(ID.idNulo) )
                            continue;
                        
                        // Ingresar Tiempo
                        double dias = pedirDouble( "Ingrese el tiempo, en dias, que se tarda el Barco "
                                + "en llegar al Puerto: ");
                        nuevaRuta.agregarPuerto(idPuertoNuevo, (float)dias);
                        idPuertoAnterior = idPuertoNuevo;
                        
                    }while( pedirDecision( "¿Desea ingresar otro Puerto (S/N)? ") );
                    
                    // Agregar Ruta
                    if( nuevaRuta.obtenerPuertos().length > 1 )
                        return registroRutas.agregarRuta(nuevaRuta);
                    else
                        return ID.idNulo;
                    
                }
                default:
                    return ID.idNulo;
            }
        }catch (InterruptionException ex){
            return ID.idNulo;
        }
    }
    
    /**
     * Permite escoger un Objeto de cualquier tipo registrable, mostrando
     * todas las opciones al usuario.
     * @param tipoObjeto
     * @return ID del objeto que se escogio.
     */
    public ID escogerObjeto( ObjetoRegistrable tipoObjeto ){
        try{
            ArrayList<Object> identificadores = new ArrayList<Object>();
            ArrayList<String> datosObjeto = new ArrayList<String>();
            int maxTamanoID = 0;
            
            // Llenar datos
            switch(tipoObjeto){
                case Navieras:
                {
                    String[] nombres = registroNavieras.obtenerNombreDeNavieras();
                    for (String nombreActual : nombres) {
                        identificadores.add(nombreActual);
                        maxTamanoID = Math.max(maxTamanoID, nombreActual.length());
                        
                        Naviera n = registroNavieras.obtenerNaviera(nombreActual);
                        datosObjeto.add(n.toString());
                    }
                    break;
                }
                case Puertos:
                {
                    String[] nombres = registroPuertos.obtenerNombresDePuertos();
                    for (String nombreActual : nombres) {
                        identificadores.add(nombreActual);
                        maxTamanoID = Math.max(maxTamanoID, nombreActual.length());
                        
                        Puerto p = registroPuertos.obtenerPuerto(nombreActual);
                        datosObjeto.add(p.toString());
                    }
                    break;
                }
                case Barcos:
                {
                    String[] nombres = registroBarcos.obtenerNombresDeBarcos();
                    
                    for (String nombreActual : nombres) {
                        identificadores.add(nombreActual);
                        maxTamanoID = Math.max(maxTamanoID, nombreActual.length());
                        
                        Barco b = registroBarcos.obtenerBarco(nombreActual);
                        datosObjeto.add(b.toString());
                    }
                    break;
                }
                case Contenedores:
                {
                    Contenedor[] contenedores =
                            registroContenedores.obtenerTodosContenedores();
                    for (Contenedor contActual : contenedores) {
                        ID id = registroContenedores.obtenerID(contActual);
                        identificadores.add(id);
                        maxTamanoID = Math.max(maxTamanoID, id.toString().length());
                        
                        datosObjeto.add(contActual.toString());
                    }
                    break;
                }
                case Rutas:
                {
                    Ruta[] rutas = registroRutas.obtenerTodasRutas();
                    for (Ruta rutaActual : rutas) {
                        ID id = registroRutas.obtenerID(rutaActual);
                        identificadores.add(id);
                        maxTamanoID = Math.max(maxTamanoID, id.toString().length());
                        
                        datosObjeto.add(rutaActual.toString());
                    }
                    break;
                }
            }
            
            // Verificar que hayan opciones
            if( identificadores.isEmpty() ){
                System.out.println("<No hay " +
                        tipoObjeto.toString() + " registrados. " +
                        "Presione Enter para continuar>");
                ingreso.nextLine();
                return ID.idNulo;
            }
            
            // Mostrar las opciones disponibles
            System.out.println("<Presione Enter para desplegar el listado de "
                    + tipoObjeto.toString() + ">");
            ingreso.nextLine();
            
            for (int actual = 0; actual < identificadores.size(); actual++) {
                // ID
                System.out.print("(" + actual + ")\t");
                
                // Identificador e informacion
                String identificador = identificadores.get(actual).toString();
                System.out.print( identificador );
                
                /* Agregar espacios */
                for (int i = identificador.length(); i < maxTamanoID; i++)
                    System.out.print(" ");
                
                System.out.println(" - " + datosObjeto.get(actual) );
            }
            
            // Pedir opcion
            int opcion = pedirInt( "Opcion: ", 0, identificadores.size() - 1 );
            
            switch( tipoObjeto ){
                case Navieras:
                    return registroNavieras.obtenerID(
                            identificadores.get(opcion).toString());
                case Puertos:
                    return registroPuertos.obtenerID(
                            identificadores.get(opcion).toString());
                case Barcos:
                    return registroBarcos.obtenerID(
                            identificadores.get(opcion).toString());
                case Contenedores:
                case Rutas:
                    return (ID)identificadores.get(opcion);
                default:
                    return ID.idNulo;
            }
            
        }catch(InterruptionException ex){
            return ID.idNulo;
        }
        
    }
    
    /**
     * Muestra todos los objetos del tipo especificado.
     * @param tipoObjeto 
     */
    public void mostrarRegistroObjeto( ObjetoRegistrable tipoObjeto ){
        ArrayList<Object> identificadores = new ArrayList<Object>();
        ArrayList<String> datosObjeto = new ArrayList<String>();
        int maxTamanoID = 0;
            
        switch(tipoObjeto){
            case Navieras:
            {
                String[] nombres = registroNavieras.obtenerNombreDeNavieras();
                for (String nombreActual : nombres) {
                    identificadores.add(nombreActual);
                    maxTamanoID = Math.max(maxTamanoID, nombreActual.length());

                    Naviera n = registroNavieras.obtenerNaviera(nombreActual);
                    datosObjeto.add(n.toString());
                }
                break;
            }
            case Puertos:
            {
                String[] nombres = registroPuertos.obtenerNombresDePuertos();
                for (String nombreActual : nombres) {
                    identificadores.add(nombreActual);
                    maxTamanoID = Math.max(maxTamanoID, nombreActual.length());

                    Puerto p = registroPuertos.obtenerPuerto(nombreActual);
                    datosObjeto.add(p.toString());
                }
                break;
            }
            case Barcos:
            {
                String[] nombres = registroBarcos.obtenerNombresDeBarcos();

                for (String nombreActual : nombres) {
                    identificadores.add(nombreActual);
                    maxTamanoID = Math.max(maxTamanoID, nombreActual.length());

                    Barco b = registroBarcos.obtenerBarco(nombreActual);
                    datosObjeto.add(b.toString());
                }
                break;
            }
            case Contenedores:
            {
                Contenedor[] contenedores =
                        registroContenedores.obtenerTodosContenedores();
                for (Contenedor contActual : contenedores) {
                    ID id = registroContenedores.obtenerID(contActual);
                    identificadores.add(id);
                    maxTamanoID = Math.max(maxTamanoID, id.toString().length());

                    datosObjeto.add(contActual.toString());
                }
                break;
            }
            case Rutas:
            {
                Ruta[] rutas = registroRutas.obtenerTodasRutas();
                for (Ruta rutaActual : rutas) {
                    ID id = registroRutas.obtenerID(rutaActual);
                    identificadores.add(id);
                    maxTamanoID = Math.max(maxTamanoID, id.toString().length());

                    datosObjeto.add(rutaActual.toString());
                }
                break;
            }
        }
        
        // Verificar que hayan opciones
            if( identificadores.isEmpty() ){
                System.out.println("<No hay " +
                        tipoObjeto.toString() + " registrados. " +
                        "Presione Enter para continuar>");
                ingreso.nextLine();
                return;
            }
        
        // Mostrar las opciones disponibles
        System.out.println("<Presione Enter para desplegar el listado de "
                + tipoObjeto.toString() + ">");
        ingreso.nextLine();

        for (int actual = 0; actual < identificadores.size(); actual++) {
            // Identificador e informacion
            String identificador = identificadores.get(actual).toString();
            System.out.print( identificador );

            /* Agregar espacios */
            for (int i = identificador.length(); i < maxTamanoID; i++)
                System.out.print(" ");

            // Datos
            System.out.println(" - " + datosObjeto.get(actual) );
        }
        
        System.out.println("---------------------------------------------------");
        mostrarPausa("");
    }
    
    public enum ObjetoRegistrable{
        Navieras,
        Puertos,
        Barcos,
        Contenedores,
        Rutas
    }
    
    /**
     * Muestra un mensaje al usuario y luego espera a que presione
     * Enter para continuar.
     * @param mensaje Mensaje a desplegar.
     */
    public void mostrarPausa( String mensaje ){
        if( !mensaje.isEmpty() )
            System.out.println(mensaje);
        System.out.println("<Presione Enter para continuar>");
        ingreso.nextLine();
    }
    
    /**
     * Permite el ingreso de datos para la localización de un puerto.
     * 
     * @throws InterruptionException Esta excepción es lanzada en el momento
     * que el usuario ingresa la palabra de finalización (FINAL). Con lo cual
     * desea terminar de ingresar datos.
     */
    public Localizacion pedirLocalizacion() throws InterruptionException
    {
        String pais=pedirString( "Ingrese el país donde se localiza el puerto: ");
        String mensajeLatitud="Ingrese la latitud: ";
        String mensajeLongitud="Ingrese la longitud: ";

        Localizacion nuevaLocalizacion=new Localizacion(pais,
                pedirDouble(mensajeLatitud),
                pedirDouble(mensajeLongitud));
        return nuevaLocalizacion;
    }
    
    /**
     * Pide un nombre único al usuario que no esté contenido en nombresUtilizados.
     * @param pregunta
     * @param nombresUtilizados
     * @return
     * @throws Interface.Dialogos.InterruptionException 
     */
    public String pedirNombre( String pregunta, String[] nombresUtilizados )
            throws InterruptionException{
        
        // Pedir un nombre unico al usuario
        ArrayList<String> utilizados = new ArrayList<String>();
        utilizados.addAll(Arrays.asList(nombresUtilizados));
        
        String nuevoNombre = "";
        while( nuevoNombre.equals("") ){
            String nombre = pedirString( pregunta );
            if( utilizados.contains(nombre) )
                System.out.println("\t**ERROR**: El nombre " + nombre +
                        " ya se encuentra registrado." );
            else
                nuevoNombre = nombre;
        }
        
        return nuevoNombre;
    }
    
    /**
     * Hace una pregunta al usuario para una decisión si o no.
     * @param pregunta Pregunta al usuario.
     * @return true si el usuario escoge 'si', false si escoge 'no'.
     * @throws InterruptionException Esta excepción es lanzada en el momento
     * que el usuario ingresa la palabra de finalización (FINAL). Con lo cual
     * desea terminar de ingresar datos.
     */
    public boolean pedirDecision( String pregunta ){
        try{
            // Pedir dato
            while(true){
                System.out.print(pregunta);
                String opcion = ingreso.nextLine();
                if( opcion.equalsIgnoreCase("S") || opcion.equalsIgnoreCase("SI") )
                    return true;
                else if( opcion.equalsIgnoreCase("N") || opcion.equalsIgnoreCase("NO") )
                    return false;
                else if( opcion.equalsIgnoreCase( "FINAL" ) )
                    throw new InterruptionException();
                else
                    System.out.println("**ERROR**: Debe de escoger si o no");
            }
        }catch( InterruptionException ex ){
            return false;
        }
    }
    
    /**
     * Controla el ingreso de números enteros a la vez que defiende en caso de
     * ingreso erróneo de datos.
     * 
     * @throws InterruptionException Esta excepción es lanzada en el momento
     * que el usuario ingresa la palabra de finalización (FINAL). Con lo cual
     * desea terminar de ingresar datos.
     * @param pregunta String con el que se le pide al usuario que ingrese
     * los datos.
     * @param min Valor mínimo, inclusivo, que puede recibirse.
     * @param max Valor máximo, inclusivo, que puede recibirse.
     * @return Un entero válido en el rango [min,max]
     */
    public int pedirInt( String pregunta, int min, int max ) 
            throws InterruptionException
    {
        // Pedir dato
        while( true ){
            // Hacer pregunta
            System.out.print(pregunta);

            if( ingreso.hasNextInt() ){
                int dato = ingreso.nextInt();
                ingreso.nextLine();
                if( dato < min || dato > max ){
                    System.out.println("\tERROR: El dato debe estar entre [" + min +
                            ", " + max + "]" );
                    continue;
                }
                else
                    return dato;
            } else {
                if( ingreso.nextLine().equalsIgnoreCase(FINALIZACION) )
                    throw new InterruptionException();
                else
                    System.out.println("\tERROR: Ingrese un entero válido.");
            }
        }
    }
    
    /**
     * Controla el ingreso de números tipo double a la vez que defiende en caso
     * de algún ingreso de datos erróneo.
     * 
     * @throws InterruptionException Esta excepción es lanzada en el momento
     * que el usuario ingresa la palabra de finalización (FINAL). Con lo cual
     * desea terminar de ingresar datos.
     * @param pregunta
     * @return
     * 
     */
    public double pedirDouble(String pregunta)
            throws InterruptionException
    {
        //Pedir dato
        while(true)
        {
            //Hacer pregunta
            System.out.print(pregunta);

            if( ingreso.hasNextDouble() ){
                double dato=ingreso.nextDouble();
                ingreso.nextLine();
                return dato;
            } else {
                if( ingreso.nextLine().equalsIgnoreCase(FINALIZACION) )
                    throw new InterruptionException();
                else
                    System.out.println("\tERROR: Ingrese un dato válido");
            }
        }
    }
    
    /**
     * Controla el ingreso de datos de tipo String.
     * 
     * @param pregunta
     * @return
     * @throws InterruptionException Esta excepción es lanzada en el momento
     * que el usuario ingresa la palabra de finalización (FINAL). Con lo cual
     * desea terminar de ingresar datos.
     */
    public String pedirString( String pregunta ) 
            throws InterruptionException{
        // Hacer pregunta
        System.out.print(pregunta);
        String texto = ingreso.nextLine();
        if( texto.equalsIgnoreCase( FINALIZACION ) )
            throw new InterruptionException();
        else
            return texto;
    }
    
    /**
     * Esta función permite ingresar una fecha 
     * @param in
     * @param mensaje
     * @return
     * @throws Interface.ConsolaUI.InterruptionException 
     */
    public Date pedirFecha(String mensaje)
            throws InterruptionException
    {
        System.out.println(mensaje);
        //Calendar calendario;
        int año = pedirInt("\tAño:", 2011, 2020);
        
        int mes = pedirInt("\tMes: ", 1, 12);
        mes=mes-1;
        int dia = pedirInt("\tDia: ", 1, 31);
        GregorianCalendar fecha = new GregorianCalendar(año, mes, dia);
        
        return fecha.getTime();
    }
    
    /**
     * Esta excepción es lanzada para indicar que el usuario ingresó FINAL
     * en algún momento. Con lo cual indica que quiere finalizar el ingreso
     * de datos y regresar al menu inmediatamente superior.
     */
    public static class InterruptionException extends Exception{
    }
    
    /**
     * Punto de entrada para la Prueba de Dialogos
     * @param args 
     */
    public static void main( String[] args ){
        Registros registros = new Registros();
        Scanner ingreso = new Scanner(System.in);
        
        Dialogos d = new Dialogos( ingreso, registros );
        System.out.println("NAVIERA");
        System.out.println(d.pedirObjeto(ObjetoRegistrable.Navieras));
        
        System.out.println("PUERTO");
        System.out.println(d.pedirObjeto(ObjetoRegistrable.Puertos));
        
        System.out.println("BARCO");
        System.out.println(d.pedirObjeto(ObjetoRegistrable.Barcos));
        
        System.out.println("CONTENEDOR");
        System.out.println(d.pedirObjeto(ObjetoRegistrable.Contenedores));
        
        System.out.println("RUTA");
        System.out.println(d.pedirObjeto(ObjetoRegistrable.Rutas));
        
        System.out.println("ESCOGER CONTENEDOR");
        System.out.println(d.escogerObjeto(ObjetoRegistrable.Contenedores));
    }
}
